﻿namespace MSSqlSchemaDoc.Provider.MicrosoftSqlServer
{
    using System;
    using System.Collections.ObjectModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using MSSqlSchemaDoc.Core.DataStructures;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Helpers;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Resources;

    /// <summary>
    /// Contains the helper methods for user defined functions.
    /// </summary>
    public partial class SqlScriptingManager
    {
        /// <summary>
        /// Construct a user defined function object.
        /// </summary>
        /// <param name="reader">Use the data in the data reader starting at it's current position.</param>
        /// <param name="hasMoreFunctions">This method will return false if this is the last table in the result set.</param>
        /// <returns>The constructed procedure.</returns>
        private UserDefinedFunction ConstructUserDefinedFunction(IDataReader reader, ref bool hasMoreFunctions)
        {
            // Create the table object and set properties
            UserDefinedFunction f = new UserDefinedFunction()
            {
                Name = DataReaderHelper.GetString(reader, "ObjectName"),
                Schema = DataReaderHelper.GetString(reader, "SchemaName")
            };

            f.ObjectId = DataReaderHelper.GetInt(reader, "ObjectId").Value;

            f.Definition = DataReaderHelper.GetString(reader, "Definition");

            // Add the parameters
            if (reader["ParameterName"] != DBNull.Value)
            {
                f.Parameters = new Collection<ProcedureParameter>();
                f.Parameters.Add(ConstructProcedureParameter(reader));
            }

            // Get all the parameter for the procedure by looping through the data reader
            string currentId = reader["ObjectId"].ToString();
            while ((hasMoreFunctions = reader.Read()) && currentId == reader["ObjectId"].ToString())
            {
                f.Parameters.Add(ConstructProcedureParameter(reader));
            }

            // Documentation
            if (this.IncludeDocumentationObjects)
            {
                f.Documentation = this.ConstructUserDefinedFunctionDocumentation(f);
            }

            return f;
        }

        /// <summary>
        /// Fetches a list of UDF's and extracts the information for each.
        /// </summary>
        /// <param name="connection">An open connection to the database.</param>
        /// <param name="objectType">The UDF object type.</param>
        private void ScriptUserDefinedFunctions(SqlConnection connection, DataStructureObjectType objectType)
        {
            // Load data
            StringBuilder query = new StringBuilder(this.queryManager.UserDefinedFunctions());
            this.InsertWhereClause(query);
            SqlCommand c = new SqlCommand(query.ToString(), connection);

            using (IDataReader reader = c.ExecuteReader())
            {
                // Build the functions
                bool hasData;
                while (hasData = reader.Read())
                {
                    while (hasData)
                    {
                        string name = GetObjectName(reader);
                        this.OnBeginScriptingObject(name);

                        UserDefinedFunction f = this.ConstructUserDefinedFunction(reader, ref hasData);
                        this.OnCompleteScriptingObject(f, name);
                    }
                }
            }
        }

        /// <summary>
        /// Constructs the documentation object for a UDF.
        /// </summary>
        /// <param name="function">The UDF to document.</param>
        /// <returns>The documentation object.</returns>
        private UserDefinedFunctionDocumentation ConstructUserDefinedFunctionDocumentation(UserDefinedFunction function)
        {
            UserDefinedFunctionDocumentation existingDocumentation;
            UserDefinedFunctionDocumentation documentation =
                this.ConstructDocumentationObject<UserDefinedFunctionDocumentation>(out existingDocumentation, function.Name, function.Schema);

            // Parameters
            foreach (ProcedureParameter p in function.Parameters)
            {
                ParameterDocumentation pd = new ParameterDocumentation() { Name = p.Name };
                pd.Documentation = GetDocumentationFromCollection<ParameterDocumentation>(existingDocumentation.Parameters, pd.Name, pd.Schema);
                documentation.Parameters.Add(pd);
            }

            return documentation;
        }
    }
}
